// scaup - Scala up!
// (c) 2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Parser.scala 4 2009-11-06 19:59:06Z normen.mueller $
package locutor.parsing.model.rs

import java.io.File

import collection.mutable.HashSet

import scaup.Views._

import locutor.model.RuleSystem

/** A rule system parser.
 *
 * At the moment only primitive string representations of rule systems are supported.
 * No need for sophisticated AST yet due to direct looping into GrGEN.
 * 
 * Parsing restrictions:
 * - HEX numbers
 * - ``#include''
 * 
 * @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 * @version 0.0.1 */
private [rs] class Parser extends XGRSParser with ExpressionParser {
  /* locutor models root path */
  private val LOCUTOR_RULES = System.getProperty("locutor.rules").toFile
  
  /* Fill in abstract defs*/
  type Tokens = Lexer
  val lexical: Tokens = new Lexer
  
  /* Configure lexical parsing */                                 
  lexical.reserved ++= new HashSet[String] ++ List(    
    "alternative",
    "delete", "dpo", 
    "emit", "emitpre", "emitpost", "eval", "exact", "exec", "extends",
    "hom",
    "if", "independent", "induced", "iterated",
    "map", "modify", "multiple",
    "negative",
    "optional", 
    "pattern", "patternpath",
    "replace", "return", "rule", "rulesystem",
    "set", 
    "test",
    "using",
    "var"
  ) ++ XGRSParser.reserved ++ ExpressionParser.reserved
  
  lexical.delimiters ++= new HashSet[String] ++ List(
    "{", "}",
    "<-", "->", "?-", "-?",
    ";", "\\"
  ) ++ XGRSParser.delimiters ++ ExpressionParser.delimiters
  
  
  /* Define the grammar */
  lazy val rs: Parser[RuleSystem] = ("rulesystem" ~> ident) ~ opt(upper) ~ ("{" ~> stmts <~ "}") ^^ {
    case name ~ upper ~ stmts => new RuleSystem(name, stmts) ++ upper      
  }
  
  private lazy val upper = "extends" ~> (ident ~ (("," ~> ident)*)) ^^ {
    case u ~ us => (u :: us) map (r => phrase(rs)(new lexical.Scanner((LOCUTOR_RULES / (r.toLowerCase + ".rs")).content)) match {
      case Success(result, _) => result
      case n@_                => throw new Exception("RS parsing error: " + n)
    })
  }
  
  private lazy val stmts = (rule*) ^^ { case rules => rules }
  
  private lazy val rule = opt("exact" | "induced" | "dpo") ~ (test_stmt | rule_stmt | pattern_stmt) ^^ { case m ~ r => (m match {case Some(m) => m + " " case _ => ""}) + r }  
  
  private lazy val test_stmt = ("test" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern)*) <~ "}") ^^ {
    case id ~ params ~ rts ~ ps => "test " +  id + ∑(params) + ∑(rts) + "{" + ∑(ps) + "}"
  }

  private lazy val rule_stmt = ("rule" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern)*)) ~ ((replace | modify) <~ "}") ^^ {
    case id ~ params ~ rts ~ ps ~ rm => "rule " + id + ∑(params) + ∑(rts) + "{" + ∑(ps) + rm + "}"
  }

  private lazy val pattern_stmt =
    (("pattern" ~> ident) ~ opt(params) ~ opt(return_types) ~ ("{" ~> ((pattern_body)*)) ~ (opt(replace | modify) <~ "}") ^^ {
      case id ~ params ~ rts ~ ps ~ rm => "pattern " + id + ∑(params) + ∑(rts) + "{" + ∑(ps) + ∑(rm) + "}"}
    | pattern_body
    )

  private lazy val pattern =
    ( ("pattern" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ {case ps => "pattern{" + ∑(ps) + "}"}
    | pattern_body
    )

  private lazy val pattern_body: Parser[String] = graphlet | alternative | iterated | negative | independent | condition | return_stmt | hom | exact | induced

  private lazy val graphlet =
    ( (node ~ opt(continuation)) ^^ {case n ~ c => n + ∑(c) + ";"}
    | continuation               ^^ {case c      => c + ";"}
    ) <~ ";"

  private lazy val alternative = "alternative" ~ "{" ~ ((alternative_body)+) ~ "}" ^^ {case abs => "alternative{" + abs + "}"}

  private lazy val alternative_body = (ident <~ "{") ~ ((pattern_body)*) ~ (opt(replace | modify) <~ "}") ^^ { case id ~ ps ~ rm => id+"{" + ∑(ps) + ∑(rm) + "}" }

  private lazy val iterated = ("iterated" | "optional" | "multiple") ~ (opt(ident) <~ "{") ~ ((pattern_body)*) ~ (opt(replace | modify) <~ "}") ^^ {
    case m ~ id ~ ps ~ rm => m+" " + ∑(id) + "{" + ∑(ps) + ∑(rm) + "}"
  }

  //def negative = ("negative" ~ "{") ~> ((("patternpath" | "pattern") ~ ";")*) ~ (((pattern_body)*) <~ "}")
  private lazy val negative = ("negative" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ { case pb => "negative{" + ∑(pb) + "}" }

  //def independent = "independent" ~ "{" ~ ((("patternpath" | "pattern") ~ ";")*) ~ ((pattern_body)*) ~ "}"
  private lazy val independent = ("independent" ~ "{") ~> (((pattern_body)*) <~ "}") ^^ { case ps => "independent{" + ∑(ps) + "}" }

  private lazy val condition = ("if" ~ "{") ~> (((expr <~ ";")*) <~ "}") ^^ { case exprs => "if{" + ((exprs map (_ + ";")) mkString) + "}" }

  private lazy val hom = ("hom" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";" ^^ { case id ~ ids => (id :: ids) mkString ("hom(", ",", ");") }

  private lazy val exact = ("exact" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";"  ^^ { case id ~ ids => (id :: ids) mkString ("exact(", ",", ");") }

  private lazy val induced = ("induced" ~ "(") ~> (ident ~ ((("," ~> ident)*) <~ ")")) <~ ";" ^^ { case id ~ ids => (id :: ids) mkString ("induced(", ",", ");") }

  private lazy val replace = ("replace" ~> opt(ident)) ~ opt(params) ~ ("{" ~> ((replace_body)*) <~ "}") ^^ { case id ~ ps ~ rbs => "replace " + ∑(id) + ∑(ps) + "{" + ∑(rbs) + "}" }

  private lazy val replace_body =  (graphlet | eval | return_stmt | exec | emit)

  private lazy val modify = ("modify" ~> opt(ident)) ~ opt(params) ~ ("{" ~> ((modify_body)*) <~ "}") ^^ { case id ~ ps ~ mbs => "modify " + ∑(id) + ∑(ps) + "{" + ∑(mbs) + "}" }

  private lazy val modify_body = (graphlet | eval | delete | return_stmt | exec | emit)

  private lazy val node =
    ( (opt(annotated_ident | annotation) <~ ":") ~ ( (ident ~ args) ^^ {case id ~ as => id+as}
                                                   | (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~> ident <~ ">") ^^ {
                                                     case id ~ tc ~ rt => id+ ∑(tc) + (rt match {case Some(rt) => "<"+rt+">" case _=> ""})
                                                   }
                                                   )                ^^ {case id ~ t  => ∑(id) + ":" + t}
    | ident ~ opt(args)                                             ^^ {case id ~ as => id + ∑(as)}
    | ("." ~> opt(annotation))                                      ^^ {case as      => "." + ∑(as)}
    )

  private lazy val edge = 
    ( ("<-" | "-") ~ opt(edge_refinement) ~ ("->" | "-") ^^ {case l ~ e ~ r => l + ∑(e) + r}
    | ("?-" ~ opt(edge_refinement) ~ "-?")               ^^ {case l ~ e ~ r => l + ∑(e) + r}
    )

  private lazy val edge_refinement = 
    ( (opt(annotated_ident) <~ ":") ~ (ident | typeOf) ~ opt(type_constraint) ~ opt("<" ~> ident <~ ">") ^^ {
      case v ~ t ~ tc ~ rt => ∑(v) + ":" + t + ∑(tc) + (rt match {case Some(rt) => "<"+rt+">" case _=> ""})
    }
    | ident
    )

  private lazy val continuation: Parser[String] = edge ~ opt(node ~ opt(continuation)) ^^ {
    case e ~ Some(n ~ Some(c)) => e+n+c
    case e ~ Some(n ~ None)    => e+n
    case e ~ None              => e
  }

  private lazy val return_stmt = ("return" ~ "(") ~> (expr ~ (("," ~> expr)*)) <~ (")" ~ ";") ^^ { case expr ~ exprs => (expr :: exprs) mkString ("return(", ",", ");") }

  private lazy val params = "(" ~> opt(param ~ (("," ~> param)*)) <~ ")" ^^ {
    case Some(p ~ ps) => (p :: ps) mkString ("(", "," , ")")
    case None         => "()"
  }

  private lazy val param = (edge | variable | node)

  private lazy val args = ("(" ~> opt(expr ~ (("," ~> expr)*))) <~ ")" ^^ {
    case Some(expr ~ exprs) => (expr :: exprs) mkString ("(", ",", ")")
    case None               => "()"
  }

  private lazy val type_constraint =
    ( "\\" ~> ident                              ^^ {case id  => "\\"+id}
    | "\\" ~> ("(" ~> repsep(ident, "+") <~ ")") ^^ {case ids => ids mkString ("\\(", "+", ")")}
    )

  private lazy val return_types = (":" ~ "(") ~> (opt((ident | primitives) ~ ((("," ~> (ident | primitives))*))) <~ ")") ^^ {
    case Some(t ~ ts) => (t :: ts) mkString (":(", ",", ")")
    case None         => ":()"
  }

  private lazy val variable = "var" ~> (ident <~ ":") ~
    ( ident | primitives
    | ("map" ~ "<") ~> (ident <~ ",") ~ (ident <~ ">") ^^ {case f ~ s => "map<"+f+","+"s"+">"}
    | ("set" ~ "<") ~> (ident <~ ">")                  ^^ {case f     => "set<"+f+">"}
    ) ^^ {case id ~ t => "var "+id+":"+t}
               
  private lazy val delete = ("delete" ~ "(") ~> (ident ~ (("," ~> ident)*)) <~ (")" ~ ";") ^^ { case id ~ ids => "delete("+((id :: ids) mkString ",")+");" }

  private lazy val exec = ("exec" ~ "(") ~> xgrs <~ (")" ~ ";") ^^ { case xgrs => "exec("+xgrs+");" }

  private lazy val emit = ("emit" | "emitpre" | "emitpost") ~ ("(" ~> expr) ~ (("," ~> expr)*) <~ (")" ~ ";") ^^ { case e ~ expr ~ exprs => e+"("+((expr :: exprs) mkString ",")+");" }

  private lazy val eval = ("eval" ~ "{") ~> (((assignment <~ ";")*) <~ "}") ^^ { case as => "eval{"+((as map (_ + ";")) mkString)+"}" }

  private lazy val assignment = (((ident <~ ".") ~ ident) ^^ {case c ~ m => c+"."+m} | visited) ~ ("=" ~> expr) ^^ { case l ~ r => l+"="+r }
  
  override lazy val string = accept("string",  {case lexical.StringLit(n) => n mkString ("\"", "", "\"") })

}